package plus;

import java.util.Arrays;

import plus.run.VM;

public class B extends M_System {
	public static final Type _void = Type.fresh(new S_Intrinsic("void"));
	public static final Type _bool = _primitive("bool");
	public static final Type _real = _primitive("real");
	public static final Type _int = _primitive("int");
	public static final Type _str = _primitive("string");

	public static final Type _array = Type.fresh(new S_Array(Type.variable()));
	private static final Type _nil = Type.fresh(new S_Nil());
	static Type arrayOf(Type t) { return Type.fresh(new S_Array(t)); }
	
	private static Type _primitive(String name) { return Type.fresh(new S_Intrinsic(name)); }
	private void add(String name, Type t) {
		TS_Intrinsic td = new TS_Intrinsic(name, t);
		Symbol<TypeSym> sym = new Symbol<TypeSym>(name, 0, td); 
		types.enter(sym);
	}

	public B() {
		// Enter types into the type dictionary
		add("bool", _bool);
		add("real", _real);
		add("int", _int);
		add("string", _str);
		
		// Enter functions into the function dictionary
		attach(new SProc("print", new Type[] {_str}, _void) {
			public void call() { System.out.print(VM.pop()); }
		});
		attach(new SProc("print", new Type[] {_int}, _void) {
			public void call() { System.out.print(VM.pop()); }
		});
		attach(new SProc("print", new Type[] {_real}, _void) {
			public void call() { System.out.print(VM.pop()); }
		});
		attach(new SProc("print", new Type[] {_bool}, _void) {
			public void call() { System.out.print(VM.pop()); }
		});
		attach(new SProc("chr", new Type[] {_int}, _str) {
			public void call() {
				VM.stack[VM.sp] = new String(Character.toChars((Integer)VM.stack[VM.sp]));
			}
		});
		attach(new SProc("mid", new Type[] {_str, _int, _int}, _str) {
			public void call() {
				int len = (Integer) VM.pop();
				int left = (Integer) VM.pop();
				String str = (String) VM.pop();
				VM.push(str.substring(left, left + len));
			}
		});
		attach(new SProc("len", new Type[] {_str}, _int) {
			public void call() {
				String str = (String) VM.pop();
				VM.push(str.length());
			}
		});
		
		fe.enter(new Ins("+", VM.ADI, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("+", VM.ADR, new Type[] {_real, _real}, _real).sym);
		fe.enter(new Ins("+", VM.ADS, new Type[] {_str, _str}, _str).sym);
		fe.enter(new Ins("-", VM.SBI, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("-", VM.SBR, new Type[] {_real, _real}, _real).sym);
		fe.enter(new Ins("*", VM.MLI, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("*", VM.MLR, new Type[] {_real, _real}, _real).sym);
		fe.enter(new Ins("div", VM.DVI, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("mod", VM.MDI, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("mod", VM.MDR, new Type[] {_real, _real}, _real).sym);
		fe.enter(new Ins("/", VM.DVR, new Type[] {_real, _real}, _real).sym);
		fe.enter(new Ins("^", VM.POW, new Type[] {_real, _real}, _real).sym);
		
		fe.enter(new Ins("-", VM.NGI, new Type[] {_int}, _int).sym);
		fe.enter(new Ins("-", VM.NGR, new Type[] {_real}, _real).sym);
		
		fe.enter(new Ins("&", VM.AND, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("|", VM.OR, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("~", VM.EOR, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("~", VM.BNOT, new Type[] {_int}, _int).sym);
		
		fe.enter(new Ins("<",  VM.LTI, new Type[] {_int, _int}, _bool).sym);
		fe.enter(new Ins("<=", VM.LEI, new Type[] {_int, _int}, _bool).sym);
		fe.enter(new Ins("=", VM.EQI, new Type[] {_int, _int}, _bool).sym);
		fe.enter(new Ins("!=", VM.NEI, new Type[] {_int, _int}, _bool).sym);
		fe.enter(new Ins(">",  VM.GTI, new Type[] {_int, _int}, _bool).sym);
		fe.enter(new Ins(">=", VM.GEI, new Type[] {_int, _int}, _bool).sym);
		
		fe.enter(new Ins("<",  VM.LTR, new Type[] {_real, _real}, _bool).sym);
		fe.enter(new Ins("<=", VM.LER, new Type[] {_real, _real}, _bool).sym);
		fe.enter(new Ins("=", VM.EQR, new Type[] {_real, _real}, _bool).sym);
		fe.enter(new Ins("!=", VM.NER, new Type[] {_real, _real}, _bool).sym);
		fe.enter(new Ins(">",  VM.GTR, new Type[] {_real, _real}, _bool).sym);
		fe.enter(new Ins(">=", VM.GER, new Type[] {_real, _real}, _bool).sym);
		
		fe.enter(new Ins("<",  VM.LTS, new Type[] {_str, _str}, _bool).sym);
		fe.enter(new Ins("<=", VM.LES, new Type[] {_str, _str}, _bool).sym);
		fe.enter(new Ins("=", VM.EQS, new Type[] {_str, _str}, _bool).sym);
		fe.enter(new Ins("!=", VM.NES, new Type[] {_str, _str}, _bool).sym);
		fe.enter(new Ins(">",  VM.GTS, new Type[] {_str, _str}, _bool).sym);
		fe.enter(new Ins(">=", VM.GES, new Type[] {_str, _str}, _bool).sym);
		
		fe.enter(new Ins("shr", VM.LSR, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("shrx", VM.ASR, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("shl", VM.SHL, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("rol", VM.ROL, new Type[] {_int, _int}, _int).sym);
		fe.enter(new Ins("ror", VM.ROR, new Type[] {_int, _int}, _int).sym);
		
		fe.enter(new Ins("sqrt", VM.SQRT, new Type[] {_real}, _real).sym);
		fe.enter(new Ins("sin", VM.SIN, new Type[] {_real}, _real).sym);
		fe.enter(new Ins("cos", VM.COS, new Type[] {_real}, _real).sym);
		fe.enter(new Ins("tan", VM.TAN, new Type[] {_real}, _real).sym);
		fe.enter(new Ins("atan", VM.ATN, new Type[] {_real}, _real).sym);
		fe.enter(new Ins("exp", VM.EXP, new Type[] {_real}, _real).sym);
		fe.enter(new Ins("ln", VM.LOG, new Type[] {_real}, _real).sym);
		
		fe.enter(new Ins("len", VM.LEN, new Type[] {_array}, _int).sym);
//		fe.enter(new Ins("or", VM.ORA, new Type[] {_bool, _bool}, _bool).sym);
//		fe.enter(new Ins("xor", VM.EOR, new Type[] {_bool, _bool}, _bool).sym);
//		fe.enter(new Ins("not", VM.NOT, new Type[] {_bool}, _bool).sym);
		attach(new SProc("array", new Type[] {_int}, arrayOf(_nil)) {
			public void call() { VM.push(new Object[(int)VM.pop()]); }
		});
		attach(new SProc("array", new Type[] {_int}, arrayOf(_int)) {
			public void call() {
				Object[] A = new Object[(int)VM.pop()];
				Arrays.fill(A, 0);
				VM.push(A);
			}
		});
		attach(new SProc("array", new Type[] {_int}, arrayOf(_real)) {
			public void call() {
				Object[] A = new Object[(int)VM.pop()];
				Arrays.fill(A, 0.0);
				VM.push(A);
			}
		});
		attach(new SProc("array", new Type[] {_int}, arrayOf(_str)) {
			public void call() {
				Object[] A = new Object[(int)VM.pop()];
				Arrays.fill(A, "");
				VM.push(A);
			}
		});
		Type tv = Type.variable();
		attach(new SProc("array", new Type[] {_int, tv}, arrayOf(tv)) {
			public void call() {
				Object O = VM.pop();
				int I = (int)VM.pop();
				Object[] A = new Object[I];
				Arrays.fill(A, O);
				VM.push(A);
			}
		});
		tv = Type.variable();
		attach(new SProc("fill", new Type[] {arrayOf(tv), tv}, _void) {
			public void call() {
				Object O = VM.pop();
				Object[] A = (Object[])VM.pop();
				Arrays.fill(A, O);
			}
		});
		attach(new SProc("int", new Type[] {_real}, _int) {
			public void call() { VM.stack[VM.sp] = Integer.valueOf((int)VM.stack[VM.sp]) ; }
		});
		attach(new SProc("real", new Type[] {_int}, _real) {
			public void call() { VM.stack[VM.sp] = Double.valueOf((int)VM.stack[VM.sp]); }
		});
		attach(new SProc("str", new Type[] {_real}, _str) {
			public void call() { VM.stack[VM.sp] = VM.stack[VM.sp].toString(); }
		});
		attach(new SProc("str", new Type[] {_int}, _str) {
			public void call() { VM.stack[VM.sp] = VM.stack[VM.sp].toString(); }
		});
	
	}

}
